home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Text / Show / Less / less-252 / command.c < prev    next >
C/C++ Source or Header  |  1994-11-06  |  23KB  |  1,248 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  * User-level command processor.
  30.  */
  31.  
  32. #include "less.h"
  33. #include "position.h"
  34. #include "option.h"
  35. #include "cmd.h"
  36.  
  37. extern int erase_char, kill_char;
  38. extern int ispipe;
  39. extern int sigs;
  40. extern int quit_at_eof;
  41. extern int hit_eof;
  42. extern int sc_width;
  43. extern int sc_height;
  44. extern int swindow;
  45. extern int jump_sline;
  46. extern int quitting;
  47. extern int wscroll;
  48. extern int nohelp;
  49. extern int top_scroll;
  50. extern int ignore_eoi;
  51. extern char *every_first_cmd;
  52. extern char version[];
  53. extern struct scrpos initial_scrpos;
  54. extern IFILE curr_ifile;
  55. #if CMD_HISTORY
  56. extern void *ml_search;
  57. extern void *ml_examine;
  58. #if SHELL_ESCAPE || PIPEC
  59. extern void *ml_shell;
  60. #endif
  61. #else
  62. /* No CMD_HISTORY */
  63. #define    ml_search    NULL
  64. #define    ml_examine    NULL
  65. #define    ml_shell      NULL
  66. #endif
  67. #if EDITOR
  68. extern char *editor;
  69. extern char *editproto;
  70. #endif
  71. extern int screen_trashed;    /* The screen has been overwritten */
  72.  
  73. static char ungot[100];
  74. static char *ungotp = NULL;
  75. #if SHELL_ESCAPE
  76. static char *shellcmd = NULL;    /* For holding last shell command for "!!" */
  77. #endif
  78. static int mca;            /* The multicharacter command (action) */
  79. static int search_type;        /* The previous type of search */
  80. static int number;        /* The number typed by the user */
  81. static char optchar;
  82. static int optflag;
  83. #if PIPEC
  84. static char pipec;
  85. #endif
  86.  
  87. static void multi_search();
  88.  
  89. /*
  90.  * Move the cursor to lower left before executing a command.
  91.  * This looks nicer if the command takes a long time before
  92.  * updating the screen.
  93.  */
  94.     static void
  95. cmd_exec()
  96. {
  97.     lower_left();
  98.     flush();
  99. }
  100.  
  101. /*
  102.  * Set up the display to start a new multi-character command.
  103.  */
  104.     static void
  105. start_mca(action, prompt, mlist)
  106.     int action;
  107.     char *prompt;
  108.     void *mlist;
  109. {
  110.     mca = action;
  111.     clear_bot();
  112.     cmd_putstr(prompt);
  113. #if CMD_HISTORY
  114.     set_mlist(mlist);
  115. #endif
  116. }
  117.  
  118.     public int
  119. in_mca()
  120. {
  121.     return (mca != 0 && mca != A_PREFIX);
  122. }
  123.  
  124. /*
  125.  * Set up the display to start a new search command.
  126.  */
  127.     static void
  128. mca_search()
  129. {
  130.     switch (SRCH_DIR(search_type))
  131.     {
  132.     case SRCH_FORW:
  133.         mca = A_F_SEARCH;
  134.         break;
  135.     case SRCH_BACK:
  136.         mca = A_B_SEARCH;
  137.         break;
  138.     }
  139.  
  140.     clear_bot();
  141.  
  142.     if (search_type & SRCH_FIRST_FILE)
  143.         cmd_putstr("@");
  144.  
  145.     if (search_type & SRCH_PAST_EOF)
  146.         cmd_putstr("*");
  147.  
  148.     if (search_type & SRCH_NOMATCH)
  149.         cmd_putstr("!");
  150.  
  151.     switch (SRCH_DIR(search_type))
  152.     {
  153.     case SRCH_FORW:
  154.         cmd_putstr("/");
  155.         break;
  156.     case SRCH_BACK:
  157.         cmd_putstr("?");
  158.         break;
  159.     }
  160. #if CMD_HISTORY
  161.     set_mlist(ml_search);
  162. #endif
  163. }
  164.  
  165. /*
  166.  * Execute a multicharacter command.
  167.  */
  168.     static void
  169. exec_mca()
  170. {
  171.     register char *cbuf;
  172.  
  173.     cmd_exec();
  174.     cbuf = get_cmdbuf();
  175.  
  176.     switch (mca)
  177.     {
  178.     case A_F_SEARCH:
  179.     case A_B_SEARCH:
  180.         multi_search(cbuf, number);
  181.         break;
  182.     case A_FIRSTCMD:
  183.         /*
  184.          * Skip leading spaces or + signs in the string.
  185.          */
  186.         while (*cbuf == '+' || *cbuf == ' ')
  187.             cbuf++;
  188.         if (every_first_cmd != NULL)
  189.             free(every_first_cmd);
  190.         if (*cbuf == '\0')
  191.             every_first_cmd = NULL;
  192.         else
  193.             every_first_cmd = save(cbuf);
  194.         break;
  195.     case A_OPT_TOGGLE:
  196.         toggle_option(optchar, cbuf, optflag);
  197.         optchar = '\0';
  198.         break;
  199.     case A_F_BRACKET:
  200.         match_brac(cbuf[0], cbuf[1], 1, number);
  201.         break;
  202.     case A_B_BRACKET:
  203.         match_brac(cbuf[1], cbuf[0], 0, number);
  204.         break;
  205.     case A_EXAMINE:
  206.         edit_list(cbuf);
  207.         break;
  208. #if SHELL_ESCAPE
  209.     case A_SHELL:
  210.         /*
  211.          * !! just uses whatever is in shellcmd.
  212.          * Otherwise, copy cmdbuf to shellcmd,
  213.          * expanding any special characters ("%" or "#").
  214.          */
  215.         if (*cbuf != '!')
  216.         {
  217.             if (shellcmd != NULL)
  218.                 free(shellcmd);
  219.             shellcmd = fexpand(cbuf);
  220.             if (shellcmd == NULL)
  221.                 break;
  222.         }
  223.  
  224.         if (shellcmd == NULL)
  225.             lsystem("");
  226.         else
  227.             lsystem(shellcmd);
  228.         error("!done", NULL_PARG);
  229.         break;
  230. #endif
  231. #if PIPEC
  232.     case A_PIPE:
  233.         (void) pipe_mark(pipec, cbuf);
  234.         error("|done", NULL_PARG);
  235.         break;
  236. #endif
  237.     }
  238. }
  239.  
  240. /*
  241.  * Add a character to a multi-character command.
  242.  */
  243.     static int
  244. mca_char(c)
  245.     int c;
  246. {
  247.     char *p;
  248.     int flag;
  249.     char buf[3];
  250.  
  251.     switch (mca)
  252.     {
  253.     case 0:
  254.         /*
  255.          * Not in a multicharacter command.
  256.          */
  257.         return (NO_MCA);
  258.  
  259.     case A_PREFIX:
  260.         /*
  261.          * In the prefix of a command.
  262.          * This not considered a multichar command
  263.          * (even tho it uses cmdbuf, etc.).
  264.          * It is handled in the commands() switch.
  265.          */
  266.         return (NO_MCA);
  267.  
  268.     case A_DIGIT:
  269.         /*
  270.          * Entering digits of a number.
  271.          * Terminated by a non-digit.
  272.          */
  273.         if ((c < '0' || c > '9') && 
  274.           editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE) == A_INVALID)
  275.         {
  276.             /*
  277.              * Not part of the number.
  278.              * Treat as a normal command character.
  279.              */
  280.             number = cmd_int();
  281.             mca = 0;
  282.             cmd_accept();
  283.             return (NO_MCA);
  284.         }
  285.         break;
  286.  
  287.     case A_OPT_TOGGLE:
  288.         /*
  289.          * Special case for the TOGGLE_OPTION command.
  290.          * If the option letter which was entered is a
  291.          * single-char option, execute the command immediately,
  292.          * so user doesn't have to hit RETURN.
  293.          * If the first char is + or -, this indicates
  294.          * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
  295.          */
  296.         if (c == erase_char || c == kill_char)
  297.             break;
  298.         if (optchar != '\0' && optchar != '+' && optchar != '-')
  299.             /*
  300.              * We already have the option letter.
  301.              */
  302.             break;
  303.         switch (c)
  304.         {
  305.         case '+':
  306.             optflag = OPT_UNSET;
  307.             break;
  308.         case '-':
  309.             optflag = OPT_SET;
  310.             break;
  311.         default:
  312.             optchar = c;
  313.             if (optflag != OPT_TOGGLE || single_char_option(c))
  314.             {
  315.                 toggle_option(c, "", optflag);
  316.                 return (MCA_DONE);
  317.             }
  318.             break;
  319.         }
  320.         if (optchar == '+' || optchar == '-')
  321.         {
  322.             optchar = c;
  323.             break;
  324.         }
  325.         /*
  326.          * Display a prompt appropriate for the option letter.
  327.          */
  328.         if ((p = opt_prompt(c)) == NULL)
  329.         {
  330.             buf[0] = '-';
  331.             buf[1] = c;
  332.             buf[2] = '\0';
  333.             p = buf;
  334.         }
  335.         start_mca(A_OPT_TOGGLE, p, (void*)NULL);
  336.         return (MCA_MORE);
  337.  
  338.     case A_F_SEARCH:
  339.     case A_B_SEARCH:
  340.         /*
  341.          * Special case for search commands.
  342.          * Certain characters as the first char of 
  343.          * the pattern have special meaning:
  344.          *    !  Toggle the NOMATCH flag
  345.          *    *  Toggle the PAST_EOF flag
  346.          *    @  Toggle the FIRST_FILE flag
  347.          */
  348.         if (len_cmdbuf() > 0)
  349.             /*
  350.              * Only works for the first char of the pattern.
  351.              */
  352.             break;
  353.  
  354.         flag = 0;
  355.         switch (c)
  356.         {
  357.         case '!':
  358.             flag = SRCH_NOMATCH;
  359.             break;
  360.         case '@':
  361.             flag = SRCH_FIRST_FILE;
  362.             break;
  363.         case '*':
  364.             flag = SRCH_PAST_EOF;
  365.             break;
  366.         }
  367.         if (flag != 0)
  368.         {
  369.             search_type ^= flag;
  370.             mca_search();
  371.             return (MCA_MORE);
  372.         }
  373.         break;
  374.     }
  375.  
  376.     /*
  377.      * Any other multicharacter command
  378.      * is terminated by a newline.
  379.      */
  380.     if (c == '\n' || c == '\r')
  381.     {
  382.         /*
  383.          * Execute the command.
  384.          */
  385.         exec_mca();
  386.         return (MCA_DONE);
  387.     }
  388.     /*
  389.      * Append the char to the command buffer.
  390.      */
  391.     if (cmd_char(c) == CC_QUIT)
  392.         /*
  393.          * Abort the multi-char command.
  394.          */
  395.         return (MCA_DONE);
  396.  
  397.     if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
  398.     {
  399.         /*
  400.          * Special case for the bracket-matching commands.
  401.          * Execute the command after getting exactly two
  402.          * characters from the user.
  403.          */
  404.         exec_mca();
  405.         return (MCA_DONE);
  406.     }
  407.  
  408.     /*
  409.      * Need another character.
  410.      */
  411.     return (MCA_MORE);
  412. }
  413.  
  414. /*
  415.  * Display the appropriate prompt.
  416.  */
  417.     static void
  418. prompt()
  419. {
  420.     register char *p;
  421.  
  422.     if (ungotp != NULL && ungotp > ungot)
  423.     {
  424.         /*
  425.          * No prompt necessary if commands are from 
  426.          * ungotten chars rather than from the user.
  427.          */
  428.         return;
  429.     }
  430.  
  431.     /*
  432.      * If nothing is displayed yet, display starting from initial_scrpos.
  433.      */
  434.     if (empty_screen())
  435.     {
  436.         if (initial_scrpos.pos == NULL_POSITION)
  437.             /*
  438.              * {{ Maybe this should be:
  439.              *    jump_loc(ch_zero(), jump_sline);
  440.              *    but this behavior seems rather unexpected 
  441.              *    on the first screen. }}
  442.              */
  443.             jump_loc(ch_zero(), 1);
  444.         else
  445.             jump_loc(initial_scrpos.pos, initial_scrpos.ln);
  446.     } else if (screen_trashed)
  447.     {
  448.         int save_top_scroll;
  449.         save_top_scroll = top_scroll;
  450.         top_scroll = 1;
  451.         repaint();
  452.         top_scroll = save_top_scroll;
  453.     }
  454.  
  455.     /*
  456.      * If the -E flag is set and we've hit EOF on the last file, quit.
  457.      */
  458.     if (quit_at_eof == 2 && hit_eof && 
  459.         next_ifile(curr_ifile) == NULL_IFILE)
  460.         quit(0);
  461.  
  462.     /*
  463.      * Select the proper prompt and display it.
  464.      */
  465.     clear_bot();
  466.     p = pr_string();
  467.     if (p == NULL)
  468.         putchr(':');
  469.     else
  470.     {
  471.         so_enter();
  472.         putstr(p);
  473.         so_exit();
  474.     }
  475. }
  476.  
  477.     public void
  478. dispversion()
  479. {
  480.     PARG parg;
  481.  
  482.     parg.p_string = version;
  483.     error("less  version %s", &parg);
  484. }
  485.  
  486. /*
  487.  * Get command character.
  488.  * The character normally comes from the keyboard,
  489.  * but may come from ungotten characters
  490.  * (characters previously given to ungetcc or ungetsc).
  491.  */
  492.     public int
  493. getcc()
  494. {
  495.     if (ungotp == NULL)
  496.         /*
  497.          * Normal case: no ungotten chars, so get one from the user.
  498.          */
  499.         return (getchr());
  500.  
  501.     if (ungotp > ungot)
  502.         /*
  503.          * Return the next ungotten char.
  504.          */
  505.         return (*--ungotp);
  506.  
  507.     /*
  508.      * We have just run out of ungotten chars.
  509.      */
  510.     ungotp = NULL;
  511.     if (len_cmdbuf() == 0 || !empty_screen())
  512.         return (getchr());
  513.     /*
  514.      * Command is incomplete, so try to complete it.
  515.      */
  516.     switch (mca)
  517.     {
  518.     case A_DIGIT:
  519.         /*
  520.          * We have a number but no command.  Treat as #g.
  521.          */
  522.         return ('g');
  523.  
  524.     case A_F_SEARCH:
  525.     case A_B_SEARCH:
  526.         /*
  527.          * We have "/string" but no newline.  Add the \n.
  528.          */
  529.         return ('\n'); 
  530.  
  531.     default:
  532.         /*
  533.          * Some other incomplete command.  Let user complete it.
  534.          */
  535.         return (getchr());
  536.     }
  537. }
  538.  
  539. /*
  540.  * "Unget" a command character.
  541.  * The next getcc() will return this character.
  542.  */
  543.     public void
  544. ungetcc(c)
  545.     int c;
  546. {
  547.     if (ungotp == NULL)
  548.         ungotp = ungot;
  549.     if (ungotp >= ungot + sizeof(ungot))
  550.     {
  551.         error("ungetcc overflow", NULL_PARG);
  552.         quit(1);
  553.     }
  554.     *ungotp++ = c;
  555. }
  556.  
  557. /*
  558.  * Unget a whole string of command characters.
  559.  * The next sequence of getcc()'s will return this string.
  560.  */
  561.     public void
  562. ungetsc(s)
  563.     char *s;
  564. {
  565.     register char *p;
  566.  
  567.     for (p = s + strlen(s) - 1;  p >= s;  p--)
  568.         ungetcc(*p);
  569. }
  570.  
  571. /*
  572.  * Search for a pattern, possibly in multiple files.
  573.  * If SRCH_FIRST_FILE is set, begin searching at the first file.
  574.  * If SRCH_PAST_EOF is set, continue the search thru multiple files.
  575.  */
  576.     static void
  577. multi_search(pattern, n)
  578.     char *pattern;
  579.     int n;
  580. {
  581.     register int nomore;
  582.     IFILE save_ifile;
  583.     int changed_file;
  584.  
  585.     changed_file = 0;
  586.     save_ifile = curr_ifile;
  587.  
  588.     if (search_type & SRCH_FIRST_FILE)
  589.     {
  590.         /*
  591.          * Start at the first (or last) file 
  592.          * in the command line list.
  593.          */
  594.         if (SRCH_DIR(search_type) == SRCH_FORW)
  595.             nomore = edit_first();
  596.         else
  597.             nomore = edit_last();
  598.         if (nomore)
  599.             return;
  600.         changed_file = 1;
  601.         search_type &= ~SRCH_FIRST_FILE;
  602.     }
  603.  
  604.     for (;;)
  605.     {
  606.         if ((n = search(search_type, pattern, n)) == 0)
  607.             /*
  608.              * Found it.
  609.              */
  610.             return;
  611.  
  612.         if (n < 0)
  613.             /*
  614.              * Some kind of error in the search.
  615.              * Error message has been printed by search().
  616.              */
  617.             break;
  618.  
  619.         if ((search_type & SRCH_PAST_EOF) == 0)
  620.             /*
  621.              * We didn't find a match, but we're
  622.              * supposed to search only one file.
  623.              */
  624.             break;
  625.         /*
  626.          * Move on to the next file.
  627.          */
  628.         if (SRCH_DIR(search_type) == SRCH_BACK)
  629.             nomore = edit_prev(1);
  630.         else
  631.             nomore = edit_next(1);
  632.         if (nomore)
  633.             break;
  634.         changed_file = 1;
  635.     }
  636.  
  637.     /*
  638.      * Didn't find it.
  639.      * Print an error message if we haven't already.
  640.      */
  641.     if (n > 0)
  642.         error("Pattern not found", NULL_PARG);
  643.  
  644.     if (changed_file)
  645.     {
  646.         /*
  647.          * Restore the file we were originally viewing.
  648.          */
  649.         if (edit_ifile(save_ifile))
  650.             quit(-1);
  651.     }
  652. }
  653.  
  654. /*
  655.  * Main command processor.
  656.  * Accept and execute commands until a quit command.
  657.  */
  658.     public void
  659. commands()
  660. {
  661.     register int c;
  662.     register int action;
  663.     register char *cbuf;
  664.     int save_search_type;
  665.     char *s;
  666.     char tbuf[2];
  667.     PARG parg;
  668.  
  669.     search_type = SRCH_FORW;
  670.     wscroll = (sc_height + 1) / 2;
  671.  
  672.     for (;;)
  673.     {
  674.         mca = 0;
  675.         cmd_accept();
  676.         number = 0;
  677.         optchar = '\0';
  678.  
  679.         /*
  680.          * See if any signals need processing.
  681.          */
  682.         if (sigs)
  683.         {
  684.             psignals();
  685.             if (quitting)
  686.                 quit(-1);
  687.         }
  688.             
  689.         /*
  690.          * Display prompt and accept a character.
  691.          */
  692.         cmd_reset();
  693.         prompt();
  694.         if (sigs)
  695.             continue;
  696.         c = getcc();
  697.  
  698.     again:
  699.         if (sigs)
  700.             continue;
  701.  
  702.         /*
  703.          * If we are in a multicharacter command, call mca_char.
  704.          * Otherwise we call fcmd_decode to determine the
  705.          * action to be performed.
  706.          */
  707.         if (mca)
  708.             switch (mca_char(c))
  709.             {
  710.             case MCA_MORE:
  711.                 /*
  712.                  * Need another character.
  713.                  */
  714.                 c = getcc();
  715.                 goto again;
  716.             case MCA_DONE:
  717.                 /*
  718.                  * Command has been handled by mca_char.
  719.                  * Start clean with a prompt.
  720.                  */
  721.                 continue;
  722.             case NO_MCA:
  723.                 /*
  724.                  * Not a multi-char command
  725.                  * (at least, not anymore).
  726.                  */
  727.                 break;
  728.             }
  729.  
  730.         /*
  731.          * Decode the command character and decide what to do.
  732.          */
  733.         if (mca)
  734.         {
  735.             /*
  736.              * We're in a multichar command.
  737.              * Add the character to the command buffer
  738.              * and display it on the screen.
  739.              * If the user backspaces past the start 
  740.              * of the line, abort the command.
  741.              */
  742.             if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
  743.                 continue;
  744.             cbuf = get_cmdbuf();
  745.         } else
  746.         {
  747.             /*
  748.              * Don't use cmd_char if we're starting fresh
  749.              * at the beginning of a command, because we
  750.              * don't want to echo the command until we know
  751.              * it is a multichar command.  We also don't
  752.              * want erase_char/kill_char to be treated
  753.              * as line editing characters.
  754.              */
  755.             tbuf[0] = c;
  756.             tbuf[1] = '\0';
  757.             cbuf = tbuf;
  758.         }
  759.         s = NULL;
  760.         action = fcmd_decode(cbuf, &s);
  761.         /*
  762.          * If an "extra" string was returned,
  763.          * process it as a string of command characters.
  764.          */
  765.         if (s != NULL)
  766.             ungetsc(s);
  767.         /*
  768.          * Clear the cmdbuf string.
  769.          * (But not if we're in the prefix of a command,
  770.          * because the partial command string is kept there.)
  771.          */
  772.         if (action != A_PREFIX)
  773.             cmd_reset();
  774.  
  775.         switch (action)
  776.         {
  777.         case A_DIGIT:
  778.             /*
  779.              * First digit of a number.
  780.              */
  781.             start_mca(A_DIGIT, ":", (void*)NULL);
  782.             goto again;
  783.  
  784.         case A_F_WINDOW:
  785.             /*
  786.              * Forward one window (and set the window size).
  787.              */
  788.             if (number > 0)
  789.                 swindow = number;
  790.             /* FALLTHRU */
  791.         case A_F_SCREEN:
  792.             /*
  793.              * Forward one screen.
  794.              */
  795.             if (number <= 0)
  796.                 number = get_swindow();
  797.             cmd_exec();
  798.             forward(number, 0, 1);
  799.             break;
  800.  
  801.         case A_B_WINDOW:
  802.             /*
  803.              * Backward one window (and set the window size).
  804.              */
  805.             if (number > 0)
  806.                 swindow = number;
  807.             /* FALLTHRU */
  808.         case A_B_SCREEN:
  809.             /*
  810.              * Backward one screen.
  811.              */
  812.             if (number <= 0)
  813.                 number = get_swindow();
  814.             cmd_exec();
  815.             backward(number, 0, 1);
  816.             break;
  817.  
  818.         case A_F_LINE:
  819.             /*
  820.              * Forward N (default 1) line.
  821.              */
  822.             if (number <= 0)
  823.                 number = 1;
  824.             cmd_exec();
  825.             forward(number, 0, 0);
  826.             break;
  827.  
  828.         case A_B_LINE:
  829.             /*
  830.              * Backward N (default 1) line.
  831.              */
  832.             if (number <= 0)
  833.                 number = 1;
  834.             cmd_exec();
  835.             backward(number, 0, 0);
  836.             break;
  837.  
  838.         case A_FF_LINE:
  839.             /*
  840.              * Force forward N (default 1) line.
  841.              */
  842.             if (number <= 0)
  843.                 number = 1;
  844.             cmd_exec();
  845.             forward(number, 1, 0);
  846.             break;
  847.  
  848.         case A_BF_LINE:
  849.             /*
  850.              * Force backward N (default 1) line.
  851.              */
  852.             if (number <= 0)
  853.                 number = 1;
  854.             cmd_exec();
  855.             backward(number, 1, 0);
  856.             break;
  857.         
  858.         case A_F_FOREVER:
  859.             /*
  860.              * Forward forever, ignoring EOF.
  861.              */
  862.             cmd_exec();
  863.             jump_forw();
  864.             ignore_eoi = 1;
  865.             hit_eof = 0;
  866.             while (!ABORT_SIGS())
  867.                 forward(1, 0, 0);
  868.             ignore_eoi = 0;
  869.             break;
  870.  
  871.         case A_F_SCROLL:
  872.             /*
  873.              * Forward N lines 
  874.              * (default same as last 'd' or 'u' command).
  875.              */
  876.             if (number > 0)
  877.                 wscroll = number;
  878.             cmd_exec();
  879.             forward(wscroll, 0, 0);
  880.             break;
  881.  
  882.         case A_B_SCROLL:
  883.             /*
  884.              * Forward N lines 
  885.              * (default same as last 'd' or 'u' command).
  886.              */
  887.             if (number > 0)
  888.                 wscroll = number;
  889.             cmd_exec();
  890.             backward(wscroll, 0, 0);
  891.             break;
  892.  
  893.         case A_FREPAINT:
  894.             /*
  895.              * Flush buffers, then repaint screen.
  896.              * Don't flush the buffers on a pipe!
  897.              */
  898.             ch_flush();
  899.             if (!ispipe)
  900.                 clr_linenum();
  901.             /* FALLTHRU */
  902.         case A_REPAINT:
  903.             /*
  904.              * Repaint screen.
  905.              */
  906.             cmd_exec();
  907.             repaint();
  908.             break;
  909.  
  910.         case A_GOLINE:
  911.             /*
  912.              * Go to line N, default beginning of file.
  913.              */
  914.             if (number <= 0)
  915.                 number = 1;
  916.             cmd_exec();
  917.             jump_back(number);
  918.             break;
  919.  
  920.         case A_PERCENT:
  921.             /*
  922.              * Go to a specified percentage into the file.
  923.              */
  924.             if (number < 0)
  925.                 number = 0;
  926.             if (number > 100)
  927.                 number = 100;
  928.             cmd_exec();
  929.             jump_percent(number);
  930.             break;
  931.  
  932.         case A_GOEND:
  933.             /*
  934.              * Go to line N, default end of file.
  935.              */
  936.             cmd_exec();
  937.             if (number <= 0)
  938.                 jump_forw();
  939.             else
  940.                 jump_back(number);
  941.             break;
  942.  
  943.         case A_GOPOS:
  944.             /*
  945.              * Go to a specified byte position in the file.
  946.              */
  947.             cmd_exec();
  948.             if (number < 0)
  949.                 number = 0;
  950.             jump_line_loc((POSITION)number, jump_sline);
  951.             break;
  952.  
  953.         case A_STAT:
  954.             /*
  955.              * Print file name, etc.
  956.              */
  957.             cmd_exec();
  958.             parg.p_string = eq_message();
  959.             error("%s", &parg);
  960.             break;
  961.             
  962.         case A_VERSION:
  963.             /*
  964.              * Print version number, without the "@(#)".
  965.              */
  966.             cmd_exec();
  967.             dispversion();
  968.             break;
  969.  
  970.         case A_QUIT:
  971.             /*
  972.              * Exit.
  973.              */
  974.             quit(0);
  975.  
  976. /*
  977.  * Define abbreviation for a commonly used sequence below.
  978.  */
  979. #define    DO_SEARCH()    if (number <= 0) number = 1;    \
  980.             mca_search();            \
  981.             cmd_exec();            \
  982.             multi_search((char *)NULL, number);
  983.  
  984.  
  985.         case A_F_SEARCH:
  986.             /*
  987.              * Search forward for a pattern.
  988.              * Get the first char of the pattern.
  989.              */
  990.             search_type = SRCH_FORW;
  991.             if (number <= 0)
  992.                 number = 1;
  993.             mca_search();
  994.             c = getcc();
  995.             goto again;
  996.  
  997.         case A_B_SEARCH:
  998.             /*
  999.              * Search backward for a pattern.
  1000.              * Get the first char of the pattern.
  1001.              */
  1002.             search_type = SRCH_BACK;
  1003.             if (number <= 0)
  1004.                 number = 1;
  1005.             mca_search();
  1006.             c = getcc();
  1007.             goto again;
  1008.  
  1009.         case A_AGAIN_SEARCH:
  1010.             /*
  1011.              * Repeat previous search.
  1012.              */
  1013.             DO_SEARCH();
  1014.             break;
  1015.         
  1016.         case A_T_AGAIN_SEARCH:
  1017.             /*
  1018.              * Repeat previous search, multiple files.
  1019.              */
  1020.             search_type |= SRCH_PAST_EOF;
  1021.             DO_SEARCH();
  1022.             break;
  1023.  
  1024.         case A_REVERSE_SEARCH:
  1025.             /*
  1026.              * Repeat previous search, in reverse direction.
  1027.              */
  1028.             save_search_type = search_type;
  1029.             search_type = SRCH_REVERSE(search_type);
  1030.             DO_SEARCH();
  1031.             search_type = save_search_type;
  1032.             break;
  1033.  
  1034.         case A_T_REVERSE_SEARCH:
  1035.             /* 
  1036.              * Repeat previous search, 
  1037.              * multiple files in reverse direction.
  1038.              */
  1039.             save_search_type = search_type;
  1040.             search_type = SRCH_REVERSE(search_type);
  1041.             search_type |= SRCH_PAST_EOF;
  1042.             DO_SEARCH();
  1043.             search_type = save_search_type;
  1044.             break;
  1045.  
  1046.         case A_UNDO_SEARCH:
  1047.             undo_search();
  1048.             break;
  1049.  
  1050.         case A_HELP:
  1051.             /*
  1052.              * Help.
  1053.              */
  1054.             if (nohelp)
  1055.             {
  1056.                 bell();
  1057.                 break;
  1058.             }
  1059.             clear_bot();
  1060.             putstr(" help");
  1061.             cmd_exec();
  1062.             help(0);
  1063.             break;
  1064.  
  1065.         case A_EXAMINE:
  1066.             /*
  1067.              * Edit a new file.  Get the filename.
  1068.              */
  1069.             start_mca(A_EXAMINE, "Examine: ", ml_examine);
  1070.             c = getcc();
  1071.             goto again;
  1072.             
  1073.         case A_VISUAL:
  1074.             /*
  1075.              * Invoke an editor on the input file.
  1076.              */
  1077. #if EDITOR
  1078.             if (strcmp(get_filename(curr_ifile), "-") == 0)
  1079.             {
  1080.                 error("Cannot edit standard input", NULL_PARG);
  1081.                 break;
  1082.             }
  1083.             /*
  1084.              * Expand the editor prototype string
  1085.              * and pass it to the system to execute.
  1086.              */
  1087.             cmd_exec();
  1088.             lsystem(pr_expand(editproto, 0));
  1089.             /*
  1090.              * Re-edit the file, since data may have changed.
  1091.              * Some editors even recreate the file, so flushing
  1092.              * buffers is not sufficient.
  1093.              */
  1094.             if (edit_ifile(curr_ifile))
  1095.                 quit(-1);
  1096.             break;
  1097. #else
  1098.             error("Command not available", NULL_PARG);
  1099.             break;
  1100. #endif
  1101.  
  1102.         case A_NEXT_FILE:
  1103.             /*
  1104.              * Examine next file.
  1105.              */
  1106.             if (number <= 0)
  1107.                 number = 1;
  1108.             if (edit_next(number))
  1109.             {
  1110.                 if (quit_at_eof && hit_eof)
  1111.                     quit(0);
  1112.                 parg.p_string = (number > 1) ? "(N-th) " : "";
  1113.                 error("No %snext file", &parg);
  1114.             }
  1115.             break;
  1116.  
  1117.         case A_PREV_FILE:
  1118.             /*
  1119.              * Examine previous file.
  1120.              */
  1121.             if (number <= 0)
  1122.                 number = 1;
  1123.             if (edit_prev(number))
  1124.             {
  1125.                 parg.p_string = (number > 1) ? "(N-th) " : "";
  1126.                 error("No %sprevious file", &parg);
  1127.             }
  1128.             break;
  1129.  
  1130.         case A_INDEX_FILE:
  1131.             /*
  1132.              * Examine a particular file.
  1133.              */
  1134.             if (number <= 0)
  1135.                 number = 1;
  1136.             if (edit_index(number))
  1137.                 error("No such file", NULL_PARG);
  1138.             break;
  1139.  
  1140.         case A_OPT_TOGGLE:
  1141.             start_mca(A_OPT_TOGGLE, "-", (void*)NULL);
  1142.             optflag = OPT_TOGGLE;
  1143.             c = getcc();
  1144.             goto again;
  1145.  
  1146.         case A_DISP_OPTION:
  1147.             /*
  1148.              * Report a flag setting.
  1149.              */
  1150.             start_mca(A_DISP_OPTION, "_", (void*)NULL);
  1151.             c = getcc();
  1152.             if (c == erase_char || c == kill_char)
  1153.                 break;
  1154.             toggle_option(c, "", OPT_NO_TOGGLE);
  1155.             break;
  1156.  
  1157.         case A_FIRSTCMD:
  1158.             /*
  1159.              * Set an initial command for new files.
  1160.              */
  1161.             start_mca(A_FIRSTCMD, "+", (void*)NULL);
  1162.             c = getcc();
  1163.             goto again;
  1164.  
  1165.         case A_SHELL:
  1166.             /*
  1167.              * Shell escape.
  1168.              */
  1169. #if SHELL_ESCAPE
  1170.             start_mca(A_SHELL, "!", ml_shell);
  1171.             c = getcc();
  1172.             goto again;
  1173. #else
  1174.             error("Command not available", NULL_PARG);
  1175.             break;
  1176. #endif
  1177.  
  1178.         case A_SETMARK:
  1179.             /*
  1180.              * Set a mark.
  1181.              */
  1182.             start_mca(A_SETMARK, "mark: ", (void*)NULL);
  1183.             c = getcc();
  1184.             if (c == erase_char || c == kill_char ||
  1185.                 c == '\n' || c == '\r')
  1186.                 break;
  1187.             setmark(c);
  1188.             break;
  1189.  
  1190.         case A_GOMARK:
  1191.             /*
  1192.              * Go to a mark.
  1193.              */
  1194.             start_mca(A_GOMARK, "goto mark: ", (void*)NULL);
  1195.             c = getcc();
  1196.             if (c == erase_char || c == kill_char || 
  1197.                 c == '\n' || c == '\r')
  1198.                 break;
  1199.             gomark(c);
  1200.             break;
  1201.  
  1202. #if PIPEC
  1203.         case A_PIPE:
  1204.             start_mca(A_PIPE, "|mark: ", (void*)NULL);
  1205.             c = getcc();
  1206.             if (c == erase_char || c == kill_char)
  1207.                 break;
  1208.             if (c == '\n' || c == '\r')
  1209.                 c = '.';
  1210.             if (badmark(c))
  1211.                 break;
  1212.             pipec = c;
  1213.             start_mca(A_PIPE, "!", ml_shell);
  1214.             c = getcc();
  1215.             goto again;
  1216. #endif
  1217.  
  1218.         case A_B_BRACKET:
  1219.         case A_F_BRACKET:
  1220.             start_mca(action, "Brackets: ", (void*)NULL);
  1221.             c = getcc();
  1222.             goto again;
  1223.  
  1224.         case A_PREFIX:
  1225.             /*
  1226.              * The command is incomplete (more chars are needed).
  1227.              * Display the current char, so the user knows
  1228.              * what's going on, and get another character.
  1229.              */
  1230.             if (mca != A_PREFIX)
  1231.             {
  1232.                 start_mca(A_PREFIX, " ", (void*)NULL);
  1233.                 cmd_reset();
  1234.                 (void) cmd_char(c);
  1235.             }
  1236.             c = getcc();
  1237.             goto again;
  1238.  
  1239.         case A_NOACTION:
  1240.             break;
  1241.  
  1242.         default:
  1243.             bell();
  1244.             break;
  1245.         }
  1246.     }
  1247. }
  1248.